#!/usr/bin/env python
#
# Base class for data input system
#
# Author:   Mike Murphy (mamurph@cs.clemson.edu)
# Revision: 23 September 2009
#
#   Copyright 2009 Clemson University
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
# This material is based upon work supported under a National Science
# Foundation Graduate Research Fellowship. Any opinions, findings,
# conclusions or recommendations expressed in this publication are those of
# the author(s) and do not necessarily reflect the views of the National
# Science Foundation.

'''
Base mechanism for handling line-oriented input files. These plain-text files
consist of lines of data, with line comments possible (starting with the "#"
character). This mechanism will parse such files, stripping all comments.
Literal "#" symbols may be included in an input file using the "\\P" escape
sequence.
'''

import sys

class Data(object):
   '''
   Abstract base class for line-oriented input formats. This class must be
   subclassed to be useful.
   '''
   def __init__(self, filename, fh):
      '''
      @param filename: Name of the open file
      @type filename: str
      @param fh: Open file (r mode) to be read
      '''
      self.eof = False
      self.filename = filename
      self.ln = 0
      self.fh = fh
   # Override in subclasses:
   def record_handler(self, record):
      '''
      Receives the return value from the line_parser() method, whatever the
      return value happens to be, and handles it accordingly. In this base
      implementation, the return value from line_parser() is simply printed.
      Subclasses must override this method (unless printing to standard
      output is desired).

      @param record: Record to be handled
      '''
      print line
   #
   def line_parser(self, line, eof, filename, line_number):
      '''
      Method to parse a single line from an input data file. This base
      implementation does nothing but return the line verbatim. Subclasses
      should override this method to implement actual functionality.

      At EOF, line will be an empty string and the eof flag will be True.

      @param line: Raw line from the file, stripped of comments
                   and extra whitespace
      @type line: str
      @param eof: End of file flag
      @type eof: bool
      @param filename: Name of the input file
      @type filename: str
      @param line_number: Current line number in file
      @type line_number: int

      @return: This method may return anything desired by the subclass.
      If the return value is None, then the input line will be discarded.
      Otherwise, the return value will be treated as a record and will be
      passed verbatim to the record_handler() method.
      '''
      return line
   #
   # Should not be necessary to override:
   def parse_line(self):
      '''
      Requests the next line from the input file and invokes the line_parser
      method on it.

      @rtype: bool
      @return: True iff the file is not at EOF
      '''
      line = ''
      if not self.eof:
         line = self.fh.readline()
         if line == '':
            self.eof = True
         else:
            self.ln += 1
         line = line.split('#')[0].strip().replace('\\P', '#')
      record = self.line_parser(line, self.eof, self.filename, self.ln)
      if record is not None:
         self.record_handler(record)
      return not self.eof
   #
#
